home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-01-03 | 38.5 KB | 2,191 lines |
-
-
- UTILIDADES
-
- CARGADOR DE EJECUTABLES
-
- Esta rutina no es más que un cargador/reubicador de ficheros EXE. Los
- creadores del DOS no debieron pensar en su día que en ciertas
- ocasiones la carga de ficheros EXE mediante funciones del sistema no
- se ajusta a las necesidades de ciertas aplicaciones, como por ejemplo
- aquellas que requieran cargar un fichero EXE desde dentro de otro
- fichero.
-
- Esta rutina subsana este problema, ya que permite cargar ficheros y
- especificar en que offset dentro de éstos se encuentra el EXE (para
- una carga normal de ficheros EXE se usa un offset de 0 y ya está). No
- puede cargar ficheros con overlays ni ficheros COM, y aunque no
- funciona en el 100% de los casos, su efectividad es bastante alta.
-
- Las rutinas son para Turbo Pascal 7.0. Se compila primero la unidad
- «Exec» y después el fichero «Exectest». Su uso es sencillo:
-
- EXECTEST fichero.exe
-
- La unidad «exec» es la que sigue:
-
- UNIT Exec;
-
- INTERFACE
-
- USES
-
- Dos;
-
- VAR
-
- FicheroExe : STRING;
-
- header: ARRAY [1..28] OF BYTE;
-
- handle,PSPSegment,StartSegment:WORD;
-
- item: ARRAY [1..2] OF WORD;
-
- memory,imagesize:WORD;
-
- PROCEDURE ExecFile(pos:LONGINT);
-
-
- IMPLEMENTATION
-
- { convierte un string en formato ASCIIZ para utilizarlo con }
-
- { las funciones del DOS }
-
- PROCEDURE StringToAsciiz; ASSEMBLER;
-
- ASM
-
- mov bx,OFFSET FicheroExe
-
- xor cx,cx
-
- mov cl,[bx]
-
- @@StringToASCII:
-
- mov al,[bx+1]
-
- mov [bx],al
-
- inc bx
-
- loop @@StringToASCII
-
- mov byte ptr [bx],0
-
- END;
-
- { Carga, reubica y ejecuta un fichero .EXE }
-
- PROCEDURE ExecFile(pos:LONGINT); ASSEMBLER;
-
- ASM
-
- push es
-
- call StringToAsciiz { convierte el nombre del .exe a ASCIIZ }
-
- push ds
-
- mov ax,3d00h
-
- mov dx,OFFSET FicheroExe
-
- int 21h { abre el fichero para sólo lectura }
-
- { indexado del fichero }
-
- push ax
-
- mov bx,ax { handle del fichero }
-
- mov ax,04200h
-
- mov dx,word ptr [pos]
-
- mov cx,word ptr [pos+2]
-
- int 21h
-
- pop ax
-
- mov [handle],ax { guarda el handle del fichero }
-
- mov bx,[handle]
-
- mov dx,OFFSET header { cabecera del fichero .exe }
-
- mov cx,28
-
- mov ah,3fh
-
- int 21h { lee los 28 bytes estándar de la cabecera }
-
- pop ds
-
- mov bx,OFFSET header
-
- mov cx,[bx+08h] { longitud del header en parágrafos }
-
- mov ax,16
-
- imul cx { DX:AX -> posición de inicio de módulo }
-
- mov cx,dx
-
- mov dx,ax { CX:DX -> posición para el LSEEK }
-
- add dx,word ptr [pos]
-
- adc cx,word ptr [pos+2]
-
- mov bx,[handle]
-
- xor al,al
-
- mov ah,42h
-
- int 21h
-
- { posiciona el puntero del fichero al inicio del módulo del programa }
-
- mov bx,OFFSET header
-
- mov ax,[bx+4] { longitud total incluyendo la cabecera }
-
- inc ax { 1+ por si acaso }
-
- mov cx,32
-
- mul cx { parágrafos totales }
-
- mov [imagesize],ax { tamaño del modulo del programa }
-
- mov bx,0ffffh
-
- mov ah,48h
-
- int 21h
-
- jnc @@no_error0
-
- mov [memory],bx
-
- mov ah,48h
-
- int 21h { con esto reservamos el máximo de memoria }
-
- @no_error0:
-
- mov [PSPSegment],ax
-
- { genera un nuevo PSP...UNDOCUMENTED! }
-
- push ax
-
- mov dx,ax
-
- mov ah,055h
-
- mov si,dx
-
- add si,10h
-
- add si,[memory]
-
- { valor a escribir en el campo de memoria del PSP }
-
- int 21h
-
- pop ax
-
- mov es,ax
-
- xor di,di
-
- mov word ptr es:[di+0ah],OFFSET @@retorna
-
- mov es:[di+0ch],cs
-
- add ax,16
-
- mov [StartSegment],ax
-
- { aquí nos encargamos de leer el módulo principal del programa, que }
-
- { no es más que el código y los datos en sí... }
-
- mov bx,[handle]
-
- mov cx,[imagesize]
-
- push ds
-
- mov ds,[StartSegment]
-
- @@next_block:
-
- push cx
-
- xor dx,dx
-
- mov cx,16 { 16 bytes cada bloque }
-
- mov ah,3fh
-
- xor al,al
-
- int 21h
-
- jc @@END_read
-
- mov ax,ds
-
- inc ax
-
- mov ds,ax
-
- pop cx
-
- loop @@next_block
-
- pop ds
-
- jmp @@readgood
-
- @@END_read:
-
- pop cx
-
- pop ds
-
- @@readgood:
-
- { ahora posiciona el puntero del fichero al inicio de la tabla de }
-
- { reubicación del fichero .EXE }
-
- mov si,OFFSET header
-
- mov ax,[si+18h]
-
- { offset en bytes en el fichero de la tabla de reubicación del .exe }
-
- mov dx,ax
-
- xor cx,cx
-
- add dx,word ptr [pos] { más la posición }
-
- adc cx,word ptr [pos+2]
-
- mov bx,[handle]
-
- mov ah,42h
-
- xor al,al
-
- int 21h
-
- { ahora comienza la reubicación de verdad del fichero }
-
- mov si,OFFSET header
-
- mov cx,[si+06h] { número de datos de items de la tabla de reubicación }
-
- test cx,0ffffh
-
- jz @@no_realocate
-
- @@siguiente_item:
-
- push cx
-
- mov dx,OFFSET item
-
- mov bx,[handle]
-
- mov cx,4 { cada item se compone de offset:segmento -> 4 bytes }
-
- mov ah,3fh
-
- int 21h
-
- mov bx,OFFSET item
-
- mov ax,[StartSegment]
-
- { a cada posición indicada por el item se
-
- le suma el segmento de inicio del módulo }
-
- add ax,[bx+2]
-
- mov di,[bx]
-
- mov es,ax
-
- mov ax,es:[di]
-
- add ax,[StartSegment]
-
- mov es:[di],ax
-
- pop cx
-
- loop @@siguiente_item
-
- @@no_realocate:
-
- mov bx,[handle]
-
- mov ah,3eh
-
- int 21h { cierra el fichero }
-
- { set new PSP address...UNDOCUMENTED! }
-
- mov bx,[PSPSegment]
-
- mov ah,50h
-
- int 21h
-
- mov bx,OFFSET @@oldss
-
- cli
-
- mov cs:[bx],ss
-
- mov cs:[bx+2],sp
-
- mov cs:[bx+4],bp
-
- sti
-
- mov bx,OFFSET header
-
- mov ax,[bx+0eh]
-
- add ax,[StartSegment]
-
- cli
-
- mov ss,ax
-
- mov sp,[bx+10h] { genera SS:SP a partir del header }
-
- sti
-
- mov ax,[bx+14h]
-
- mov dx,[bx+16h]
-
- add dx,[StartSegment] { DX:AX -> CS:IP }
-
- push dx
-
- push ax
-
- mov es,[PSPSegment]
-
- mov ds,[PSPSegment]
-
- xor ax,ax { borra todos los registros }
-
- mov di,ax
-
- mov si,ax
-
- mov bx,ax
-
- mov cx,ax
-
- mov dx,ax
-
- mov bp,ax
-
- retf
-
- @@retorna:
-
- mov bx,OFFSET @@oldss
-
- cli
-
- mov ss,cs:[bx] { recupera todos los registros }
-
- mov sp,cs:[bx+2]
-
- mov bp,cs:[bx+4]
-
- sti
-
- mov ax,SEG @data
-
- mov ds,ax
-
- mov es,[PSPSegment]
-
- mov ah,49h
-
- int 21h { libera la memoria guardada por el }
-
- jmp @@restore
-
- @@oldss: dw 0
-
- @@oldsp: dw 0
-
- @@oldbp: dw 0
-
- @@restore:
-
- pop es
-
- END;
-
- BEGIN
-
- END.
-
-
- El programa «Exectest» es el sigiente:
-
-
- {$M,16000,0,0}
-
- {$F+}
-
- USES Dos, Crt, Exec;
-
- { devuelve la memoria que queda libre }
-
- FUNCTION MemoriaLibre: WORD; ASSEMBLER;
-
- ASM
-
- mov bx,0ffffh
-
- mov ah,48h
-
- int 21h
-
- mov ax,bx
-
- END;
-
- { programa principal }
-
- BEGIN
-
- WriteLn('Exec loader version 2.11');
-
- IF paramcount=0 THEN
-
- BEGIN
-
- WriteLn('Usage <filename.exe>');
-
- Halt;
-
- END;
-
- Write('Memoria libre antes de ejecutar el programa:
- ',LongInt(MemoriaLibre)*16,' bytes');
-
- FicheroExe:=paramstr(1);
-
- ExecFile(0);
-
- ASM
-
- mov ax,03
-
- int $10
-
- END;
-
- Write('Memoria libre después de salir del programa:
- ',LongInt(MemoriaLibre)*16,' bytes');
-
- END.
-
- José Cabezas García
-
- Barcelona
-
-
- ACTUALIZACION DEL PATH
-
- El siguiente programa tiene como finalidad el añadir o actualizar
- directorios en la variable de entorno «Path» de forma temporal, es
- decir, hasta que se apague el ordenador, o bien de forma definitiva
- añadiéndolo automáticamente en el «autoexec.bat».
-
- Para añadir directorios de forma temporal basta con pasarle como
- parámetros los directorios a añadir separados por espacios o por punto
- y coma (;). Si queremos que los cambios sean permanentes, esto es,
- que se actualice el «autoexec.bat», basta con poner el modificador /A
- al final del comando, justo después de los nuevos directorios que
- queremos añadir.
-
- @Echo off
-
- if "%1"=="" goto sin_args
-
- :loop
-
- if "%1"=="" goto fin_ok
-
- if "%1"=="/A" goto permanente
-
- if "%1"=="/a" goto permanente
-
- set path=%path%;%1
-
- shift
-
- goto loop
-
- :permanente
-
- echo set path=%path% >> c:\autoexec.bat
-
- goto fin
-
- :sin_args
-
- echo Sintaxis: MPATH camino [camino] ...
-
- echo Para modificar AUTOEXEC.BAT: MPATH camino [camino] ... /A
-
- goto final
-
- :fin_ok
-
- echo Nuevo PATH=%Path%
-
- :final
-
- Luis Rodríguez Beteta
-
- Madrid
-
-
-
- CAPTURADOR DE PANTALLAS ASCII
-
- El siguiente truco, realizado con Turbo Pascal, es un capturador de
- pantallas ASCII que ocupa menos de 10 Kbytes de memoria y cuyo
- funcionamiento se basa en que cuando se instala en memoria captura la
- interrupción del teclado.
-
- Para capturar una pantalla basta con pulsar la combinación «Shift-F2»,
- lo que dará lugar a que el programa genere un archivo llamado
- «Capt-XX.txt», donde XX es sustituido por un 00, 01, 02... que
- equivale al número de pantalla capturada.
-
- {$M 2000, 0, 0}
-
- {$R-, S-, I-, D+, F+,V-, V-, B-, N-, L-}
-
- Program Capturador;
-
- Uses Dos;
-
- Const IntTec = $09;
-
- Type Matriz = Array [1..2000] Of Byte;
-
- Var
-
- VecTecAnt : Pointer;
-
- Buffer : Matriz;
-
- Archivo : File Of Matriz;
-
- XW1, XW2, XW3 : Word;
-
- XS1 : Strig;
-
- Procedure CapturaPantalla;
-
- Begin
-
- If Mem[Seg0040:$0049]=7 Then XW1:=$B000 Else XW1:=$B800;
-
- XW3:=1;
-
- For XW2:=0 To 3999 Do
-
- Begin
-
- Buffer[XW3]:=Mem[XW1:XW2];
-
- Inc(XW2);
-
- Inc(XW3);
-
- End;
-
- XW1:=0;
-
- Repeat
-
- Str(Xw1:2,XS1);
-
- If XS1[1]=#32 Then XS1[1]:='0';
-
- XS1:='CAPT-'+XS1+'.TXT';
-
- Assign (Archivo,XS1);
-
- Reset(Archivo);
-
- If IOResult = 0 The Close(Archivo)
-
- Else
-
- Begin
-
- Rewrite(Archivo);
-
- Write(Archivo, Buffer);
-
- Close(archivo);
-
- Exit;
-
- End;
-
- Inc(XW1);
-
- Until False;
-
- End;
-
- Procedure Teclado (Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES,
- BP:Word); Interrupt;
-
- Begin
-
- Asm
-
- pushf
-
- call VecTecAnt
-
- End;
-
- If Port[96]=60 Then CapturaPantalla;
-
- Inline($FB);
-
- End;
-
- Begin
-
- WriteLn('Capturador de pantallas ASCII');
-
- WriteLn('Para capturar la pantalla pulsar Shift-F2');
-
- GetIntVec(IntTec,VecTecAnt);
-
- SetIntVec(IntTec,@Teclado);
-
- Keep(0);
-
- End.
-
- Bernardo Calero Castellano
-
- Tomelloso (Ciudad Real)
-
- Nota del Laboratorio: La idea de esta utilidad consiste en visualizar
- un cursor en pantalla y que podamos moverlo y situarlo donde queramos.
- En este punto apretaremos «ENTER» y podremos seguir moviendo el cursor
- para señalar los dos vértices de la zona a capturar. Una vez elegida
- la zona bastará grabar el contenido de las posiciones de memoria que
- se encuentran entre los dos vértices.
-
-
- MENSAJES POR VOZ
-
- Muchas veces resulta interesante dejar una nota a la siguiente persona
- que va a utilizar el ordenador, o simplemente un recordatorio para
- nosotros mismos. Sin embargo, lo ideal sería poder dejar un mensaje
- de viva voz. Pues bien, eso es lo que hace el programa
- «mensajes.bat».
-
- Para su correcto funcionamiento es imprescindible que se cumplan los
- siguientes requisitos:
-
- 1. El programa está ideado, en principio, para los poseedores de una
- tarjeta de sonido Sound Blaster, aunque también se pueden dejar
- mensajes por escrito.
-
- 2. Requiere la versión 6.0 ó posteriór de MS-DOS (ya que utiliza el
- comando CHOICE).
-
- 3. Se debe instalar el controlador «ANSI.SYS» (o en caso contrario
- habrá que suprimir las secuencias de escape, ya que sólo sirven para
- cambiar el color de la pantalla).
-
- 4. Los programas «VPLAY» y «VREC» de la Sond Blaster deben estar
- disponibles mediante la variable de entorno PATH. (También se puede
- especificar la ruta de acceso cada vez que aparezca una llamada a
- cualquiera de estos dos programas)
-
- 5. El programa debe ser instalado en un subdirectorio llamado
- «mensajes».
-
- Si ejecutamos la aplicación desde la línea de comandos se nos
- presentará un menú desde el que podremos grabar mensajes escritos o
- hablados, escuchar el mensaje pregrabado o borrar el mensaje
- almacenado.
-
- Si arrancamos la utilidad con la opción r («mensajes r») o incluyendo
- la llamada «call c:\mensajes\mensajes.bat r» en el «autoexec.bat», nos
- recordará los mensajes hablados y/o escritos (si es que existen) y
- después nos dará la oportunidad de volver a escucharlos o borrarlos.
- En caso de que no exista ningún mensaje esta llamada no tiene ningún
- efecto.
-
- @echo off
-
- if "%1" =="r" goto principio
-
- if "%1" =="R" goto principio
-
- :principal
-
- rem ** Rutina principal **
-
- rem **********************
-
- echo
-
- cls
-
- echo MENSAJES.BAT
-
- echo.
-
- echo OPCIONES
-
- echo.
-
- echo [1] Grabar mensaje de texto
-
- echo [2] Grabar mensaje de voz
-
- echo.
-
- echo [3] Escuchar mensaje
-
- echo.
-
- echo [4] Borrar mensaje
-
- echo.
-
- echo [0] SALIR
-
- echo.
-
- choice Opción /c:01234
-
- if errorlevel 5 goto borra
-
- if errorlevel 4 goto principio
-
- if errorlevel 3 goto grabavoz
-
- if errorlevel 2 goto grabatexto
-
- if errorlevel 1 goto end
-
- :Grabavoz
-
- cls
-
- vrec mensaje /s:11000
-
- goto principal
-
- :Grabatexto
-
- edit mensaje.txt
-
- goto principal
-
- rem ** Rutina que recuerda los mensajes y sale del programa **
-
- rem **********************************************************
-
- :principio
-
- if exist c:\mensajes\mensaje.* goto rutina
-
- echo MENSAJES.BAT: No hay mensajes para usted
-
- echo.
-
- if "%1" =="r" goto End
-
- if "%1" =="R" goto End
-
- goto principal
-
- :rutina
-
- echo
-
- cls
-
- echo MENSAJES.BAT
-
- echo Hay un mensaje para usted:
-
- echo.
-
- if not exist c:\mensajes\mensaje.txt goto voz
-
- type c:\mensajes\mensaje.txt|more
-
- :voz
-
- echo
-
- if exist c:\mensajes\mensaje.voc vplay c:\mensajes\mensaje>nul
-
- if exist c:\mensajes\mensaje.voc choice ¿Desea que le repita el
- mensaje
-
- if not exist c:\mensajes\mensaje.voc pause
-
- if errorlevel 2 goto sigue
-
- if errorlevel 1 goto principio
-
- :sigue
-
- choice ¿Borrar mensaje
-
- if errorlevel 2 goto continua
-
- if errorlevel 1 goto borra
-
- :Borra
-
- echo Borrando mensaje... Espere por favor
-
- del c:\mensajes\mensaje.txt>nul
-
- del c:\mensajes\mensaje.voc>nul
-
- if "%1" =="r" goto continua
-
- if "%1" =="R" goto continua
-
- goto principal
-
- :continua
-
- if "%1" =="r" goto End
-
- if "%1" =="R" goto End
-
- goto principal
-
- :end
-
- echo
-
- cls
-
- Gorka Elexgaray
-
- Guernica. (Vizcaya)
-
-
- PARTICION DE FICHEROS
-
- En muchas ocasiones nos encontramos ante un fichero de texto cuyas
- dimensiones hacen imposible que sea editado. Mediante esta utilidad
- escrita en Turbo Pascal, podremos dividir cualquier fichero en
- diversos ficheros del numero de líneas que deseemos. Por defecto se
- toma el número de 5.000 líneas por fichero.
-
- {$i-}
-
- Program Partidor;
-
- Var
-
- Origen,
-
- Final :Text;
-
- No_de_lineas,
-
- Actual :LongInt;
-
- FichF :String[5];
-
- NoArch,
-
- Codigo :Integer;
-
- Item :String;
-
- No_fichero :Byte;
-
- Begin
-
- No_de_lineas:=5000;
-
- If ParamCount>2 then Val(ParamStr(3), No_de_Lineas, Codigo);
-
- If (ParamCount<2) Or (Codigo<>0) then
-
- begin
-
- WriteLn('Usar: [unidad:][camino\]Partidor [unidad:]'+
-
- '[camino\]Arch_Original.ext Arch_final');
-
- Halt;
-
- End;
-
- FichF:=Copy(ParamStr(2),1,5);
-
- If No_de_lineas<512 then No_de_lineas:=512;
-
- Assign(Origen,ParamStr(1));
-
- Reset(Origen);
-
- If IOResult<>0 then
-
- begin
-
- WriteLn('Imposible abrir ',ParamStr(1));
-
- Halt(1);
-
- End;
-
- WriteLn('Fragmentando '+ParamStr(1)+'...');
-
- No_Fichero:=0;
-
- Repeat;
-
- Actual:=0;
-
- Inc(No_Fichero);
-
- Str(No_Fichero,Item);
-
- While Length(item)<3 do
-
- Item:='0'+Item;
-
- Assign(Final,ParamStr(2)+'.'+Item);
-
- Rewrite(Final);
-
- If IOResult<>0 then
-
- begin
-
- WriteLn('Imposible crear '+ParamStr(2)+'.'+Item);
-
- Halt(1);
-
- End;
-
- While Not Eof(Origen) do
-
- begin
-
- ReadLn(Origen,Item);
-
- WriteLn(Final,Item);
-
- Inc(Actual);
-
- If Actual=No_de_lineas then break;
-
- End;
-
- Close(Final);
-
- Until Eof(Origen);
-
- Close(Origen);
-
- WriteLn('Archivo '+ParamStr(1)+' fragmentado en ',No_Fichero,'Archivos.');
-
- End.
-
- Los consejos del Laboratorio:
-
- - Como ya hemos comentado, la utilidad de este programa radica en
- poder dividir ficheros de texto que a causa de su tamaño no son
- editables, en otros de menor tamaño. Una vez que hemos realizado los
- cambios deseados sobre cada unos de los ficheros más pequeños, puede
- que nos interese volver a juntar dichos archivos para tener de nuevo
- un único fichero. Para juntar todos los ficheros podemos recurrir a
- la orden «copy» del DOS, de la siguiente forma: «copy
- fich1+fich2+...+fichn fichfin»
-
- Luciano Rubio
-
- Madrid
-
-
- PARTICION DE FICHEROS MEJORADA
-
- En el truco anterior hablábamos de una utilidad que servía para
- descomponer ficheros de texto en otros de menor tamaño, para de esta
- forma poder ser editados con facilidad. Esta nueva utilidad, aunque
- básicamente hace lo mismo, tiene la ventaja de poder utilizarse tanto
- con ficheros de texto como en ficheros binarios.
-
- Su utilidad es clara, si tenemos un ejecutable que sobrepase la
- capacidad de nuestros disquetes, podemos dividirlo en diferentes
- ficheros binarios los cuales puedan ser grabados en disquetes, para
- luego resconstruirlos a su formato original.
-
- La sintaxis de uso la indica el propio programa, así como el tamaño y
- numero de ficheros creados. De igual forma, indica como regenerar el
- fichero original a partir de los trozos.
-
- program trocear;
-
- uses dos,crt;
-
- var
-
- fuente,destino:file of byte;
-
- i,veces,e:integer;
-
- tamano,c,tamanototal,j:longint;
-
- b:string[10];
-
- nombre,nombreb,nombreorigen:string[9];
-
- extension:string[3];
-
- letra:byte;
-
- fin:boolean;
-
- begin
-
- fin:=false;
-
- if paramstr(1)='' then
-
- begin
-
- writeln('troceador por ramón bernardo 93');
-
- writeln;
-
- writeln('sintaxis: trocear fichero.ext tamaÑo');
-
- writeln;
-
- writeln('donde tamaño está entre 1024 y 2147483647 bytes');
-
- writeln;
-
- writeln('por defecto el tamaño es de 360 kb');
-
- halt(1);
-
- end
-
- else
-
- begin
-
- {$i+}
-
- nombreorigen:=paramstr(1);
-
- if paramstr(2)='' then b:='367616'
-
- else b:=paramstr(2);
-
- val(b,tamano,e);
-
- if tamano<1024 then
-
- begin
-
- writeln('error:el tamaño de los subficheros ha de ser mayor de 1024');
-
- halt(1);
-
- end;
-
- tamano:=1024*(tamano div 1024);
-
- assign(fuente,paramstr(1));
-
- reset(fuente);
-
- nombreb:='';
-
- nombre:='';
-
- for j:=1 to length(nombreorigen) do
-
- if nombreorigen[j]='.' then j:=length(nombreorigen)
-
- else nombreb:=concat(nombreb,nombreorigen[j]);
-
- veces:=0;
-
- repeat
-
- str(veces,extension);
-
- repeat
-
- extension:=concat('0',extension);
-
- until length(extension)=3;
-
- nombre:=concat(nombreb,'.',extension);
-
- write('creando el fichero ',nombre);
-
- c:=0;
-
- assign(destino,nombre);
-
- rewrite(destino);
-
- while (not eof(fuente) and (c < tamano)) do
-
- begin
-
- read(fuente,letra);
-
- write(destino,letra);
-
- c:=c+1;
-
- end;
-
- writeln(' ',c,' bytes');
-
- if eof(fuente) then fin:=true;
-
- close(destino);
-
- veces:=veces+1;
-
- c:=0;
-
- until fin;
-
- close(fuente);
-
- writeln;
-
- writeln;
-
- writeln('para regenerar el fichero original teclear :');
-
- writeln;
-
- writeln('si el fichero original es ascii (texto)');
-
- writeln('copy ',nombreb,'.000+',nombreb,'.001+',nombreb,'.002+... ',nombreb,'.extension');
-
- writeln;
-
- writeln('si el fichero original es binario (exe,com,..)');
-
- writeln('copy ',nombreb,'.000 /b+',nombreb,'.001 /b+',nombreb,'.002 /b+... ',nombreb,'.extension');
-
- end;
-
- {$i-}
-
- end.
-
- Ramón B. de Bernardo Hernán
-
- Sestao. Vizcaya
-
-
- ORDENADOR PROTEGIDO
-
- Más de una vez se habrá ausentado del ordenador durante unos minutos y
- habrá querido bloquearlo para que nadie pudiera fisgar en él. O quizá
- en algún momento deseó que desapareciera el contenido de la pantalla y
- retornar a ella en cualquier momento. Este pequeño programa en C le
- propone una solución.
-
- Es una aplicación residente que se activa al pulsar una tecla o
- combinación de ellas, salvando el contenido de la pantalla (modo
- texto), borrandola y bloqueando el teclado. No se recuperará la
- pantalla inicial hasta que se pulsa la combinación de teclas elegida.
- La rutina sólo se instala una vez, ya que si se vuelve a hacer es
- ignorada.
-
- Las teclas que se usan para activarlo son Ctrl+Shift (izquierda) y
- para desactivarlo Alt+F1. Estas teclas pueden ser otras, a gusto del
- usuario, con el único problema de conocer el código correspondiente.
- Para detectar la pulsación de Ctrl+Shift (izq) se buscó en el área de
- comunicaciones de la BIOS en la dirección 0040:0017h (1 byte). Para
- Alt+F1 se ha usado la interrupción 16h de la BIOS, comprobando el
- código de exploración y el código ASCII correspondiente (si existe).
- Si el código ASCII no existe (es cero), se examina el código de
- exploración.
-
- En el caso de las teclas Alt+F1 el código ASCII correspondiente es 00,
- mientras que el código de exploración es 68h.
-
- #include <Dos.h>
-
- #include <Bios.h>
-
- #include <Stdio.h>
-
- unsigned int far *mem;
-
- void interrupt ( *oldhandler)();
-
- /* Función a la que se asigna la interrupción 9, que es la que
- intercepta*/
-
- void interrupt handler()
-
- /* Función que reemplaza a la interrupción 9*/
-
- {
-
- char ah, p_activa;
-
- static char activo = 0; /* Variable semáforo */
-
- enable(); /*Habilitar interrupciones hardware */
-
- oldhandler();
-
- if ((peekb(0x40,0x17) & 6)==6 && activo==0)
-
- /*Crtl+Shift(Izq) Pulsadas*/
-
- {
-
- activo = 1;
-
- /* Si volvemos a pulsar Crtl+Shtf(Izq) no tendrá efecto */
-
- _AH=0xf; /* Se obtiene la página de visualización activa */
-
- geninterrupt (0x10);
-
- p_activa=_BH;
-
- _AH=0x5; /* Se cambia de página activa */
-
- _AL=p_activa+3;
-
- geninterrupt (0x10);
-
- do
-
- {
-
- _AH=0; /* Se espera pulsación de tecla */
-
- geninterrupt (0x16);
-
- ah=_AH; /* Almacena código de exploración */
-
- }
-
- while(ah != 0x68); /* Mientras que no se pulse Alt+F1 */
-
- _AH=0x5; /* Se reestablece la página activa inicial */
-
- _AL=p_activa;
-
- geninterrupt (0x10);
-
- activo=0;
-
- }
-
- }
-
- void main()
-
- {
-
- /* Se comprueba que la rutina sólo se instale una vez */
-
- if( (FP_OFF(getvect(0x9))) != (FP_OFF(handler)) )
-
- {
-
- oldhandler=getvect(0x9);
-
- /* Guarda la dirección de la interrupción 9 que está en la tabla de
- vectores de int */
-
- setvect(0x9,handler);
-
- /* Asigna la dirección de nuestra rutina al vector de interrupción
- de la int 9 */
-
- printf("*** Rutina instalada *** ");
-
- mem=MK_FP(_psp,0x2c);
-
- _AX=4900;
-
- _ES=*mem;
-
- geninterrupt(0x21); /*Elimina el bloque de entorno */
-
- keep(0,_SS + (_SP/16) - _PSP); /* Deja el programa residente */
-
- }
-
- }
-
- Los consejos del Laboratorio:
-
- - Una forma de mejorar esta aplicación es introduciendo un pequeño
- programa de configuración, de forma que al instalarse se pueda
- modificar directamente las teclas que lo activan y lo desactivan.
-
- - También se le puede meter una rutina para que acepte un
- parámetro que haga que el programa se desinstale de la memoria.
-
- Oscar Bernal González
-
- Valladolid
-
-
- LA LUZ DEL DISCO DURO
-
- Mediante la siguiente rutina (escrita es Turbo Pascal 6.0) podemos
- simular en pantalla el led de estado del disco duro. Esta simulación
- puede representar grandes ventajas para aquellos usuarios que no
- pueden visualizar dicho led, por tener la cpu debajo de la mesa, por
- ejemplo.
-
- program disklighter_v4;
-
- {$m 1024,0,0}
-
- (* reserva 1k para stack. *)
-
- uses dos,crt;
-
- type rutina_tratamiento=procedure;
-
- const
-
- tiempo = 50;
-
- var
-
- rutina_original :rutina_tratamiento;
-
- interrup_hd :byte;
-
- puntero,vector :pointer;
-
- procedure nueva_rutina; interrupt;
-
- var
- i :integer;
-
- begin
-
- for i:=0 to tiempo do
-
- (* tiempo de luz. *)
-
- begin
-
- mem[$b800:156]:=ord('h');
-
- (* acceso a memoria de video. *)
-
- mem[$b800:157]:=$21;
-
- mem[$b800:158]:=ord('d');
-
- mem[$b800:159]:=$21;
-
- end;
-
- mem[$b800:157]:=$00;
-
- mem[$b800:159]:=$00;
-
- rutina_original;
-
- end;
-
- (* programa principal *)
-
- begin
-
- writeln;
-
- writeln('disklighter v4.0 residente en memoria.');
-
- interrup_hd:=$76;
-
- getintvec(interrup_hd,vector);
-
- @rutina_original:=vector;
-
- setintvec(interrup_hd,@nueva_rutina);
-
- keep($00);
-
- end.
-
- Esta rutina intercepta la interrupción que actua sobre el contador de
- interrupciones 8259, la cual se genera cada vez que se accede al disco
- duro, y redirecciona el vector que apunta a la rutina de tratamiento
- de dicha interrupción hacia esta nueva rutina, que visualiza en
- pantalla «una luz», y reenlaza despues con la rutina de tratamiento
- original.
-
- Según la velocidad de la máquina en la que se instale, deberá darse un
- valor diferente a la constante «Tiempo», por ejemplo, para un 386DX-25
- un valor adecuado es 75, y para un 386DX-33 un valor adecuado es 210.
- Si eligimos un valor de «Tiempo» demasiado bajo, apenas veremos la
- «luz» y si se elige uno muiy alto el acceso al disco duro se
- ralentiza. Es cuestión de probar el valor óptimo para nuestra
- máquina, teniendo en cuenta que cuanto mayor sea la velocidad del
- ordenador, mayor deberá ser el valor que buscamos.
-
- Luis Gascón López
-
- Valencia
-
-
- LA FECHA DEL SISTEMA
-
- En ocasiones puede ser útil la visualización de la fecha del día, pero
- la instrucción «Time» del DOS no es lo más adecuado para ello. Este
- pequeño programa en C, nos permite la visualización de la fecha en su
- formato correcto.
-
- #include <stdio.h>
-
- #include <dos.h>
-
- #include <time.h>
-
- void main()
-
- {
-
- char *day[]={"Domingo",
-
- "Lunes",
-
- "Martes",
-
- "Miércoles",
-
- "Jueves",
-
- "Viernes",
-
- "Sábado"};
-
- static char *meses[]={"Enero",
-
- "Febrero",
-
- "Marzo",
-
- "Abril",
-
- "Mayo",
-
- "Junio",
-
- "Julio",
-
- "Agosto",
-
- "Septiembre",
-
- "Octubre",
-
- "Noviembre",
-
- "Diciembre",
-
- };
-
- time_t time_t;
-
- struct date date;
-
- struct tm*tm;
-
- getdate(&date);
-
- time_t=dostounix(&date,NULL);
-
- tm=localtime(&time_t);
-
- gotoxy(45,2);
-
- cprintf(" %s ",day[tm-> tm_wday]);
-
- cprintf("%02d",date.da_day);
-
- cprintf(" de %s",meses[tm-> tm_mon]);
-
- cprintf(" de %d\n",date.da_year);
-
- }
-
- Nota de la Laboratorio Técnico:
-
- La fecha se visualiza en el estremo superior derecho de la pantalla,
- para cambiar esto basta con modificar las coordenadas que aparecen en la
- instrucción «gotoxy».
-
- Jose Angel Vidal Gallego
-
- Alacuas. Valencia
-
-
- CRONOMETRO DE PROGRAMAS
-
- El siguiente programa realizado en C, tiene la utilidad de cronometrar
- el tiempo que está un programa funcionando. Fue mandado por su autor
- como una aplicación para medir el tiempo que empleaba su programa en
- escapar del laberento, dentro del concurso de programación, pero por
- su utilidad lo hemos incluido dentro de esta sección.
-
- #include <stdio.h>
-
- #include <stdlib.h>
-
- #include <conio.h>
-
- #include <process.h>
-
- #include <time.h>
-
- void main()
-
- {
- char prog[80];
-
- int r;
-
- clock_t antes, despues;
-
- double segs;
-
- printf( "Programa a cronometrar ? " );
-
- gets( prog );
-
- antes = clock();
-
- r = _spawnlp( _P_WAIT, prog, prog, NULL );
-
- despues = clock();
-
- segs = (double)(despues - antes) / CLOCKS_PER_SEC;
-
- if( r == -1 )
-
- printf( "\nNo se pudo ejecutar :\n%s" , prog );
-
- else
-
- printf( "\n%f segundos tarda en ejecutar :\n%s" , segs , prog );
-
- exit( r );
-
- }
-
- Joaquín Obregón Cobo
-
- El Astillero. Cantabria
-
-
- LA CLAVE DE LAS BIOS AMI
-
- Aunque es una situación muy poco frecuente, algunos de nuestros
- lectores se han encontrado con la desagradable sorpresa de que al
- adquirir un ordenador con BIOS AMI (nuevo o de segunda mano) a
- personas poco fiables, el setup del mismo se encontraba protegido con
- una palabra clave que el vendedor se resistía a revelar. De esta
- forma, algunas empresas se aseguran de que el usuario sólo podrá
- modificar el hardware de su PC bajo su conocimiento, perdiendo la
- garantía o bien pagando una cantidad extra por la instalación.
-
- Tanto para este caso extremo como si simplemente se nos olvida nuestra
- propia clave, puede resultar de utilidad la siguiente rutina, que
- sirve para averiguar la password de las BIOS AMI. Para ello crearemos
- en primer lugar un fichero llamado «crackami.dbg» con el siguiente
- contenido:
-
- a
-
- jmp 118
-
- db 37
-
- mov al,[102]
-
- out 70,al
-
- jmp 10a
-
- inc byte ptr [102]
-
- cmp byte ptr [102],3f
-
- je 13f
-
- in al,71
-
- ret
-
- call 103
-
- and al,f0
-
- mov ah,al
-
- call 103
-
- or al,al
-
- jz 13f
-
- push ax
-
- xor cl,cl
-
- cmp al,ah
-
- je 138
-
- test al,e1
-
- jpe 132
-
- stc
-
- rcl al,1
-
- inc cl
-
- jnz 129
-
- mov al,cl
-
- int 29
-
- pop ax
-
- jmp 11d
-
- xor al,al
-
- ret
-
- db '// 1.1 // ARM '
-
- <línea en blanco>
-
- rcx
-
- 50
-
- n crackami.com
-
- w
-
- q
-
- <línea en blanco>
-
- A continuación damos la orden «DEBUG <crackami.dbg» para generar el
- fichero ejecutable. Al lanzar el programa «CRACKAMI.COM» así creado
- se imprimirá en pantalla la palabra clave almacenada actualmente en la
- memoria CMOS de nuestro ordenador.
-
- Antes de finalizar, hemos de hacer dos serias advertencias. La
- primera es que AMI ha modificado el método de encriptación de la clave
- en sus BIOS más recientes, las de 1994 (y en algunas del 93), de modo
- que en estos casos no funcionará la presente rutina. La segunda y más
- importante es que este programa sólo puede ser utilizado con el propio
- ordenador: es un delito emplearlo en otra máquina sin autorización
- expresa de su dueño, con el fin de tener acceso a la información
- almacenada.
-
- Arturo Ramírez-Montesinos Krogulski
-
- Boadilla del Monte (Madrid)
-
- Nota: Si el programa es incapaz de averiguar la clave almacenada en
- la CMOS, habremos de recurrir a un método «artesanal». Para ello
- llevaremos a cabo lo que se conoce como cortocircuitar la placa base,
- uniendo mediante un cable las dos patillas de la batería de la CMOS.
- Esto tiene como efecto inmediato la descarga total de todos los datos
- almacenados en la propia CMOS (parámetros de disco duro, memoria y
- disqueteras incluidas). En ciertas placas base basta con variar la
- posición de un jumper para llevar a cabo esta descarga.
-
-
- CALLAR A LA SOUND BLASTER
-
- Este truco está dirigido a quienes posean una Sound Blaster y cuyo
- sonido hubieran deseado anular en alguna ocasión sin tener que
- recurrir al potenciómetro del amplificador. La siguiente rutina,
- escrita en Borland C++ 2.0, permite reducir el volumen de dicha
- tarjeta usando la tecla Scroll Lock (o Bloq Despl).
-
- El funcionamiento del programa es muy sencillo. En primer lugar se
- accede al chip del mezclador (puerto 224h) y se selecciona el registro
- número 3, que es el que controla el volumen de los dos altavoces de la
- Sound Blaster. Luego, mediante el puerto de datos del chip, se reduce
- el volumen hasta cero o hasta «Ah» (este valor se puede cambiar a
- gusto del usuario) según el estado de la tecla Bloq Despl. Tras ello
- se deja el programa residente en memoria.
-
- El puerto base de la Sound Blaster que se utiliza en este programa es
- el 220h. Pero hay algunas tarjetas que utilizan el puerto 240h. En
- este caso, se deberá modificar el valor de la variable «BASE».
-
- #include "dos.h"
-
- void interrupt (*oldkbd)();
-
- void interrupt handler();
-
- void interrupt handler()
-
- {
-
- int tecla
-
- unsigned int BASE = 0x220;
-
- //Puerto base por defecto de la SB
-
- unsigned int CHIPBASE = BASE + 4;
-
- tecla = peek (0x0040, 0x0017);
-
- // Lee el estado del teclado
-
- if (tecla & 16)
-
- // Si la tecla Bloq Despl está encendida
-
- {
-
- asm {
-
- mov dx, CHIP
-
- // Puerto de registros del chip mezclador
-
- mov al, 3 // Volumen Master
-
- out dx, al // Seleccionar registro
-
- inc dx
-
- xor al, al // Reduce volumen
-
- out dx, al
-
- }
-
- }
-
- Else
-
- // Si está apagada...
-
- {
-
- asm {
-
- mov dx, CHIP
-
- mov al, 3
-
- out dx, al
-
- inc dx // Puerto de datos del chip mezclador
-
- xor al, 0ah // Aumento el volumen
-
- out dx, al
-
- }
-
- }
-
- oldkbd (); // Llamo a la antigua interrupción
-
- }
-
- void main (int argc, char **argv)
-
- {
-
- oldkbd=getvect(0x9);
-
- setvect(0x9, handler);
-
- printf("\nSound Blaster PRO Volume");
-
- printf("\n========================");
-
- printf("\n Programa en memoria.");
-
- printf("\n========================\n");
-
- keep(0, (_SS+(_SP/16)- _psp)); //Instala el programa
-
- }
-
-
- Eduard Sánchez Palazón
-
- Terrassa (Barcelona)
-
-
- El UTIL XCOPY
-
- Muchas veces se nos ha presentado el problema de tener que copiar del
- disco duro a una unidad de disquete, sobrepasando con lo que deseamos
- copiar el espacio del disco, teniendo que recurrir a varios disquetes
- y efectuar la copia fichero a fichero para evitar la tan temida frase
- de «espacio insuficiente».
-
- Para remediar este problema, el mismo sistema operativo nos ofrece una
- solución:
-
- Primero ponemos el atributo de archivo a todos los ficheros que
- deseemos copiar a disquete:
-
- Por ejemplo: «Attrib c:\graficos\*.* +A»
-
- Si queremos copiar también los subdirectorios podemos añadir el
- parámetro /s
-
- Introducir el disco en la unidad destino y escribir la orden de copia.
- Por ejemplo:
-
- «xcopy c:\graficos\*.* /m»
-
- Con el parámetro /m conseguimos que se vaya modificando el atributo de
- archivo segun se copian los diferentes ficheros.
-
- Ejecutaremos este último paso y el ordenador empezará a copiar hasta
- que no haya más espacio en el disco destino; entonces mostrará el
- mensaje de «espacio insuficiente en disco» en cuyo caso sustituiremos
- el disquete y repetiremos de nuevo el último paso, con lo que
- continuará copiando en el archivo en que se quedó. Si repetimos este
- paso cuantas veces sea necesario habremos copiado todos los ficheros
- que deseabamos.
-
- Joaquín Dionisio Sánchez
-
- Málaga
-
-
- INTERRUPCIONES CON QUICKBASIC
-
- El uso de interrupciones a la hora de realizar cualquier programa es
- una buena técnica para aumentar el grado de profesionalidad en éstos,
- ya que así podremos emplear servicios de la ROM-BIOS y del DOS tales
- como incluir soporte para ratón, controlar el subsistema gráfico, etc.
-
- Como sabéis, el uso de interrupciones resulta más fácil de llevar a la
- práctica en unos lenguajes que en otros, aunque no siempre se deba a
- limitaciones del propio lenguaje. En el caso de QuickBasic 4.5 parece
- que la causa reside más bien en la falta de documentación acerca de
- cómo acceder a este tipo de servicios.
-
- Desde tierras levantinas nos ha llegado la solución al problema, de
- modo que quienes deseen utilizar las interrupciones en sus programas
- simplemente tendrán que seguir los pasos que a continuación
- describimos. En primer lugar, hay que crear un archivo ASCII, llamado
- «intxs.bi», con el siguiente contenido:
-
- TYPE tReg
-
- ax AS INTEGER
-
- bx AS INTEGER
-
- cx AS INTEGER
-
- dx AS INTEGER
-
- bp AS INTEGER
-
- si AS INTEGER
-
- di AS INTEGER
-
- flags AS INTEGER
-
- ds AS INTEGER
-
- es AS INTEGER
-
- END TYPE
-
- DIM SHARED rEn AS tReg
-
- DIM SHARED rFue AS tReg
-
- DECLARE SUB INTERRUPTX(num AS INTEGER, rEn AS tReg, rFue AS tReg)
-
- A continuación, iniciaremos QuickBasic con el parámetro «/l» (por
- ejemplo, «C:\QB45\QB /l»).
-
- Al principio del programa en el que queramos acceder a alguna
- interrupción añadiremos la orden «$INCLUDE: 'intxs.bi'». (El archivo
- «intxs.bi» debe estar en el subdirectorio donde esté el QuickBasic).
-
- Para realizar una interrupción desde cualquier punto del programa
- basta con ejecutar la orden «CALL INTERRUPTX(num, rEn, rFue)», siendo
- «num» el número del servicio que queremos usar.
-
- Por lo general, tendremos que «pasar» unos parámetros para indicar qué
- función queremos usar del servicio y otros datos. En ciertas
- ocasiones también será necesario obtener el resultado de la llamada al
- servicio. Los parámetros de entrada se tienen que dejar en la
- variable «rEn», mientras que los de salida se obtienen o recogen en
- «rFue».
-
- A continuación presentamos un esquema general:
-
-
- rem parámetros de entrada (si son necesarios)
-
- rEn.ax = &Hxxxx
-
- rEn.bx = &Hyyyy
-
- rEn.cx = &Hzzzz
-
- rem llamada al servicio
-
- CALL INTERRUPTX(&Hxx,rEn,rFue)
-
- rem evaluación de la salida (si es necesario)
-
- IF rFue.ax=nn THEN <acciones>
-
- PRINT rFue.bx
-
-
- Hay que señalar que el método tiene el inconveniente de la
- imposibilidad de separar los registros AX, BX, CX y DX en sus bytes
- respectivos (AH, AL; BH, BL;...), por lo que nos veremos obligados a
- trabajar con el registro entero. También hay que advertir que la
- forma más cómoda de trabajar es en hexadecimal.
-
- Veamos a continuación cómo utilizarlo. Supongamos que tenemos que
- pasar al servicio los siguientes parámetros de entrada: «AL = x» y
- «AH = y».
-
- En primer lugar convertimos «x» e «y» a hexadecimal:
-
- «x» lo convertimos en &Hmm, e «y» lo convertimos &Hnn
-
- (de forma que «mm» y «nn» sean de 2 dígitos, rellenando con un «0» por
- la izquierda si es necesario).
-
- Los parámetros de entrada serán «rEn = &Hnnmm».
-
- Si algún byte no hay que usarlo lo llenamos con «00». Es decir, si
- por ejemplo tenemos que pasarle en BL el valor 10, la instrucción será
- «rEn.bx=&H000A».
-
- De igual forma, a la hora de obtener los resultados en «rFue» habrá
- que separar en AH, AL; BH, BL; ... El procedimiento para ello es el
- siguiente: convertimos el registro a separar (por ejemplo BX) a
- hexadecimal; obtendremos entonces 4 dígitos (si son menos se rellena
- por la izquierda con «0» hasta que sean 4). Los dos primeros, por la
- derecha, corresponderán a BH y los dos siguientes a BL. Una vez
- separados los podemos transformar a su vez en decimal o binario según
- nuestras necesidades.
-
- Sergio Ballester Lorente
-
- Godella. (Valencia)
-
-
- ALARMA RESIDENTE
-
- Delante del ordenador las horas pasan muy rápidamente y por eso
- es aconsejable disponer de un reloj con alarma a mano. ¡Qué mejor
- reloj que el interno del ordenador! Este programa es una alarma que
- avisará de la hora indicada con unos pitidos. Se queda residente y
- emplea muy poca memoria para ello. Está realizado en ensamblador y
- podemos emplear el «debug» para compilarlo.
-
- a
-
- mov ax,2528
-
- mov dx,0117
-
- int 21
-
- mov ah,49
-
- mov es,[2c]
-
- int 21
-
- mov ah,31
-
- mov dx,14
-
- int 21
-
- nop
-
- nop
-
- push ax
-
- push bx
-
- push cx
-
- push dx
-
- mov ah,02
-
- int 1a
-
- cmp ch,05 ;Horas
-
- jne 139
-
- cmp cl,05 ;minutos
-
- jne 139
-
- cmp dh,02 ;segundos
-
- jne 139
-
- mov cx,04
-
- mov al,07
-
- mov ah,0e
-
- int 10
-
- pop dx
-
- pop cx
-
- pop bx
-
- pop ax
-
- iret
-
- <Línea en blanco>
-
- rcx
-
- 3e
-
- n alarma.com
-
- w
-
- q
-
-
- Gontzal Uriarte Gómez
-
- San Juan (Alicante)
-
-
- FICHEROS «BATCH»
-
- Muchos usuarios habrán necesitado en más de una ocasión que un fichero
- batch envíe su salida a la impresora o a un fichero de texto, y
- lógicamente habrán hecho lo siguiente:
-
- Fbatch > Salida.prn
-
- Suponiendo que el fichero se llama «Fbatch.bat» y se desea volcar la
- salida al fichero «Salida.prn».
-
- Pero el funcionamiento interno del DOS no lo permite (al contrario que
- en otros sistemas operativos como Unix), ya que sólo se redirecciona
- la salida de programas «.COM» o «.EXE» que utilicen la salida
- estándar.
-
- La solución está en emular el comportamiento de sistemas operativos
- como el Unix. Veamos qué hace el Unix cuando lanza un fichero script
- (batch para MS-DOS). Lo primero que hace es cargar un nuevo shell que
- ejecute los comandos del fichero y redireccionar la salida donde se
- haya especificado o a la consola por defecto. Pues bien, esto
- traducido en MS-DOS sería:
-
- Command /C Fbatch > Salida.prn
-
- En realidad se le está diciendo que se cargue un nuevo shell
- («command.com») y se redireccione la salida estándar de éste al
- fichero «Salida.prn», pero que únicamente se ejecuten los comandos que
- hay en «Fbatch.bat» y regrese al shell anterior (parámetro /C).
-
- Gabriel Martí
-
- Barcelona
-
-
-
-
-
-
-